/*************************************************************
  Cocus Core - A simple PIC16 core emulator for PIC18
  
  version 2.0 by Santiago H. (santiagohssl@gmail.com)

  UNDER GNU LGPL

  file: main.c
*************************************************************/


/** I N C L U D E S **********************************************************/

#include <p18f4550.h>
#include <delays.h>
#include <stdio.h>
#include <stdlib.h> 
#include <string.h> 
#include <adc.h>
#include <usart.h>
#include <i2c.h>

#include "./core2/vmcore.h"
#include "./core2/service_usart.h"

#pragma config PLLDIV   = 5         // (20 MHz crystal on PICDEM FS USB board)
#pragma config CPUDIV   = OSC1_PLL2   
#pragma config USBDIV   = 2         // Clock source from 96MHz PLL/2
#pragma config FOSC     = HSPLL_HS
#pragma config FCMEN    = OFF
#pragma config IESO     = OFF
#pragma config PWRT     = OFF
#pragma config BOR      = ON
#pragma config BORV     = 3
#pragma config VREGEN   = ON      //USB Voltage Regulator
#pragma config WDT      = OFF
#pragma config WDTPS    = 32768
#pragma config MCLRE    = OFF
#pragma config LPT1OSC  = OFF
#pragma config PBADEN   = OFF
//      #pragma config CCP2MX   = ON
#pragma config STVREN   = ON
#pragma config LVP      = OFF
//      #pragma config ICPRT    = OFF       // Dedicated In-Circuit Debug/Programming
#pragma config XINST    = OFF       // Extended Instruction Set
#pragma config CP0      = OFF
#pragma config CP1      = OFF
//      #pragma config CP2      = OFF
//      #pragma config CP3      = OFF
#pragma config CPB      = OFF
//      #pragma config CPD      = OFF
#pragma config WRT0     = OFF
#pragma config WRT1     = OFF
//      #pragma config WRT2     = OFF
//      #pragma config WRT3     = OFF
#pragma config WRTB     = OFF       // Boot Block Write Protection
#pragma config WRTC     = OFF
//      #pragma config WRTD     = OFF
#pragma config EBTR0    = OFF
#pragma config EBTR1    = OFF
//      #pragma config EBTR2    = OFF
//      #pragma config EBTR3    = OFF
#pragma config EBTRB    = OFF

extern void _startup (void);        // See c018i.c in your C18 compiler dir


/** V A R I A B L E S ********************************************************/

#pragma udata BIG_BLOCK

core2_vm_1k 		vm1kb;

#pragma udata


/** P R I V A T E  P R O T O T Y P E S ***************************************/

void main(void);				// main entrypoint
void USARTInbound(void);		// usart isr


/** V E C T O R  R E M A P P I N G *******************************************/

#define USING_BOOTLOADER			0

#ifdef USING_BOOTLOADER
	/* if using bootloader, remap the reset vector */

	#define VECTOR_INT_HIGH				0x1008
	#define VECTOR_INT_LOW				0x1018

	
	#pragma code _RESET_INTERRUPT_VECTOR = 0x1000
	
	void _reset (void)
	{
		_asm goto _startup _endasm
	}
	
	#pragma code

#else
	/* if not using bootloader */
	
	#define VECTOR_INT_HIGH				0x08
	#define VECTOR_INT_LOW				0x18
#endif


/* Interrupt Vectors and ISR */
void HighISRCode();
void LowISRCode();

#pragma code _HIGH_INTERRUPT_VECTOR = VECTOR_INT_HIGH
void _high_ISR (void)
{
	_asm goto HighISRCode _endasm
}

#pragma code _LOW_INTERRUPT_VECTOR = VECTOR_INT_LOW
void _low_ISR (void)
{
	//_asm goto LowISRCode _endasm;
}

#pragma interrupt HighISRCode
void HighISRCode()
{
	if (PIR1bits.RCIF)
		USARTInbound();
}

#pragma code




/** D E C L A R A T I O N S **************************************************/

void USARTInbound(void) 
{
	unsigned char temp;

	while (PIR1bits.RCIF)
		if (RCSTAbits.CREN != 1) 
			RCSTAbits.CREN = 1; 
		else {
			temp = ReadUSART();
			WriteUSART(temp);
 			//core_periph_usart_rx(&Dev627, temp);
		}
}

char read_ext_eeprom(int address)
{
	LATEbits.LATE2 = 1; // high z output from rom
	TRISD = 0;  // output io pic
	LATEbits.LATE0 = 0;
	LATEbits.LATE1 = 0;
	
	// low byte
	LATD = address;
	LATEbits.LATE0 = 1;
	Delay1KTCYx(1);
	LATEbits.LATE0 = 0;


	// high byte
	LATD = (address>>8);
	LATEbits.LATE1 = 1;
	Delay1KTCYx(1);
	LATEbits.LATE1 = 0;
	
	
	// byte read
	TRISD = 0xff;
	LATEbits.LATE2 = 0;
	Delay1KTCYx(1);
	return PORTD;
}


void showOpcode(vm_opcode * op, unsigned int pos)
{
	char cBuff[30];
	
	sprintf(cBuff, "Execing[%x]: %hxh %hxh\r\n", pos, op->opcode.opcode, op->opcode.data);
	putsUSART(cBuff);
}

void showRegisters(vm_opcode * op)
{
	char cBuff[30];
	char z, dc, c;	
	
	if (vm_status_get_z(&vm1kb))  { z = 'Z'; } else { z = 'z'; }
	if (vm_status_get_dc(&vm1kb)) { dc = 'D'; } else { dc = 'd'; } 
	if (vm_status_get_c(&vm1kb))  { c = 'C'; } else { c = 'c'; }

	sprintf(cBuff, "W: %x STATUS: %x %c %c %c\r\n\r\n", vm_getW(&vm1kb), vm_readRam(&vm1kb, 0x3), z, dc, c);
	putsUSART(cBuff);
}

void main() 
{
	unsigned int pos;
	vm_opcode op;	
	
	TRISE = 0;
	TRISD = 0xFF;


	OpenUSART( USART_TX_INT_OFF  &
	           USART_RX_INT_OFF  &
               USART_ASYNCH_MODE &
               USART_EIGHT_BIT   &
               USART_CONT_RX     &
               USART_BRGH_HIGH,
               0x9B               );

	putrsUSART("CocusCore PIC16 VM v2.0 beta\r\n\r\n");

	INTCONbits.PEIE = 1;
	INTCONbits.GIE = 1;
	
	vm_init(&vm1kb);
	vm_services_usart_init(&vm1kb);

	
	while(1)
	{
		pos = vm_getPC(&vm1kb);

		op.opcode.opcode = read_ext_eeprom((pos*2)+1);  //samplecode[pos*2];
		op.opcode.data = read_ext_eeprom(pos*2); //samplecode[(pos*2)+1];

		/* Debug block */
		/*
		showOpcode(&vm1kb, pos);
		showRegisters(&vm1kb);
		*/

		vm_runOp(&op, &vm1kb);   // put a breakpoint here to debug	
	
		/* redirect inbound characters to guest */
		if (DataRdyUSART())
			vm_services_usart_rx(&vm1kb, ReadUSART());
	
	}
}

// Callbacks
void vm_services_usart_callback_tx(void *dev, char data)
{
	WriteUSART(data);
	while (BusyUSART());
}